﻿using System;
using ShureValidation.Language;

namespace ShureValidation.Rules
{
    /// <summary>
    /// Specifies minimum length
    /// </summary>
    /// <remarks>
    /// On a string it's the specified maximum number of letters, while on int it specifies the max number.
    /// Works on all IComparable types.
    /// <para>
    /// Language file items:
    /// <list type="table">
    ///     <listheader>
    ///         <term>ItemName</term>
    ///         <description>Language text</description>
    ///     </listheader>
    ///     <item>
    ///         <term>Min</term>
    ///         <description>'{0}' must be at least {1}.</description>
    ///     </item>
    ///     <item>
    ///         <term>MinString</term>
    ///         <description>'{0}' must be at least {1} letters.</description>
    ///     </item>
    /// </list>
    /// </para>
    /// </remarks>
    [LanguagePrompt("Min", "'{0}' must be larger or equal to {1}.")]
    [LanguagePrompt("MinString", "'{0}' must be contain {1} or more characters.")]
    [Serializable]
    public class MinRule : IRule
    {
        private readonly object _minLength;
        private object _value;

        /// <summary>
        /// Initializes a new instance of the <see cref="MinRule"/> class.
        /// </summary>
        /// <param name="value">minimum length. should match the type being validated, with the exception of string
        /// where the type should be int.</param>
        public MinRule(object value)
        {
            _minLength = value;
        }

        /// <summary>
        /// Minimum length
        /// </summary>
        public object MinLength
        {
            get { return _minLength; }
        }

        #region IRule Members

        /// <summary>
        /// Validate a field value
        /// </summary>
        /// <param name="value">Value to validate</param>
        /// <returns><c>true</c> if validation was successful; otherwise <c>false</c>.</returns>
        /// <exception cref="NotSupportedException">Value type is not supported.</exception>
        public bool Validate(object value)
        {
            if (ValueChecker.IsEmpty(value))
                return true;

            _value = value;
            if (value is string)
            {
                var s = (string) value;
                return s.Length >= (int) _minLength;
            }

            var comparable = value as IComparable;
            if (comparable != null)
                return comparable.CompareTo(_minLength) >= 0;


            throw new NotSupportedException(GetType() + " do not support " + value.GetType());
        }

        /// <summary>
        /// Format an error message
        /// </summary>
        /// <param name="fieldName">Field (have already been translated)</param>
        /// <param name="rulesLanguage">Language prompts for validation rules</param>
        /// <returns>Error message formatted for the current language.</returns>
        public string Format(string fieldName, IModelLanguage rulesLanguage)
        {
            if (_value is string)
                return string.Format(rulesLanguage["MinString"], fieldName, MinLength);
            return string.Format(rulesLanguage["Min"], fieldName, MinLength);
        }

        /// <summary>
        /// Checks if this rule support values of the specified format.
        /// </summary>
        /// <param name="type">Type of value</param>
        /// <returns><c>true</c> if the specified type can be validated; otherwise <c>false</c>.</returns>
        public bool SupportsType(Type type)
        {
            return type.GetInterface("IComparable", false) != null;
        }

        #endregion
    }
}